package com.ws.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.cors.CorsConfigurationSource;

import javax.sql.DataSource;
import java.util.Arrays;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // 最大会话数
    @Value("${spring.security.session.maximum-sessions}")
    private int maximumSessions;
    //允许跨域访问的域名列表，多个域名用逗号分隔
    @Value("${spring.security.cors.allowed-origins}")
    private String allowedOrigins;
    //允许跨域访问的HTTP方法列表，多个方法用逗号分隔
    @Value("${spring.security.cors.allowed-methods}")
    private String allowedMethods;
    //允许跨域访问的请求头列表，多个请求头用逗号分隔
    @Value("${spring.security.cors.allowed-headers}")
    private String allowedHeaders;
    //是否允许跨域请求发送凭据（如Cookie）
    @Value("${spring.security.cors.allow-credentials}")
    private String allowCredentials;
    @Value("${spring.security.login-url}")
    private String loginUrl;
    @Value("${spring.security.logout-url}")
    private String logoutUrl;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //用于配置 URL 的访问权限规则
                .authorizeRequests()
                // antMatchers用于匹配 URL 规则,
                .antMatchers("/login")
                //permitAll允许所有用户访问该 URL，不需要进行权限验证
                .permitAll()
//                .antMatchers("/**")
//                //示需要用户具有指定角色才能访问该 URL
//                .hasRole("ADMIN")
//                .antMatchers("/**")
//                //需要用户具有指定角色之一才能访问该 URL
//                .hasAnyRole("ADMIN", "USER")
//                // 其他路径需要身份验证
//                .anyRequest()
//                //需要用户进行身份认证后才能访问该 URL
//                .authenticated()
                .and()
                //启用基于表单的登录，可以指定登录页面的 URL
                .formLogin()
                //指定自定义的登录页面的 URL
                .loginPage(loginUrl)
                //指定登录表单提交的 URL 路径
                //.loginProcessingUrl("/doLogin")
                // 指定登录成功后跳转的 URL
                .defaultSuccessUrl("/home")
                //指定登录失败后跳转的 URL
                .failureUrl("/login?error=true")
                .and()
                //启用退出登录功能
                .logout()
                // 指定退出登录的 URL
                .logoutUrl(logoutUrl)
                //指定退出登录后重定向的 URL
                .logoutSuccessUrl(loginUrl)
                .and()
                //记住我的设置
                .rememberMe()
                //用于指定记住我功能的加密密钥
                .key("my-remember-me-key")
                //用于指定记住我功能的 token 存储库
                .tokenRepository(persistentTokenRepository())
                .and()
                //用于配置会话管理相关的设置
                .sessionManagement()
                // 会话失效后重定向的 URL
                .invalidSessionUrl(loginUrl)
                //最大会话数
                .maximumSessions(maximumSessions)
                //用户会话过期后重定向的
                .expiredUrl(loginUrl)
                .and()
                // 禁用会话固定保护策略
                .sessionFixation()
                // 用于配置会话迁移策略
                .migrateSession()
                //用于配置会话创建策略IF_REQUIRED: 表示仅在需要时创建会话（默认值）
                //NEVER: 表示永远不会创建会话
                //STATELESS: 表示会话是无状态的
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and()
                .csrf().disable()
                .cors();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        return tokenRepository;
    }

    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


//    @Bean
//    CorsConfigurationSource corsConfigurationSource() {
//        CorsConfiguration configuration = new CorsConfiguration();
//        configuration.setAllowCredentials(true);
//        configuration.setAllowedOrigins(Arrays.asList(allowedOrigins));
//        //configuration.addAllowedOrigin("*");
//        configuration.setAllowedMethods(Arrays.asList(allowedMethods));
//        configuration.setAllowedHeaders(Arrays.asList(allowedHeaders));
//        //configuration.addAllowedHeader("*");
//        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
//        source.registerCorsConfiguration("/**", configuration);
//        return source;
//    }
}